﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Zove.Runtime;
using System.IO;
using EmbeddedSharp.Core;

namespace Ascend.Runtime.Core {
    public class AscendClassFinder : IClassFinder {

        public Dictionary<string, ModelFile> Index {
            get;
            set;
        }

        public List<string> Workspaces {
            get;
            set;
        }

        public AscendClassFinder() {
            Index = new Dictionary<string, ModelFile>();
            Workspaces = new List<string>();
        }

        public void Scan(string directory) {
            DirectoryInfo di = new DirectoryInfo(directory);
            Scan(di);
        }

        public void Scan(DirectoryInfo di) {
            foreach (DirectoryInfo subDirectory in di.GetDirectories()) {
                Scan(subDirectory);
            }

            foreach (FileInfo file in di.GetFiles()) {
                Scan(file);
            }
        }

        public void Scan(FileInfo file) {
            string source = File.ReadAllText(file.FullName);
            AscendLexer lexer = new AscendLexer(source);

            int start = 0;
            Token token = null;
            while (lexer.HasMoreTokens()) {
                while (lexer.HasMoreTokens() && (token = lexer.NextToken()).Value != "MODEL") ;
                if (!lexer.HasMoreTokens()) {
                    break;
                }

                ModelFile model = new ModelFile();

                model.Name = lexer.NextToken().Value;

                StringBuilder body = new StringBuilder("MODEL " + model.Name);
                while (lexer.HasMoreTokens()) {
                    token = lexer.NextToken();
                    body.Append(token.Value);

                    if (token.Value == "END") {
                        token = lexer.NextToken();
                        body.Append(token.Value);

                        if (token.Value == model.Name) {
                            break;
                        }
                    }
                }
                body.Append(token.Value).Append(";");

                model.Source = body.ToString();
                model.FileName = file.FullName;

                if (!Index.ContainsKey(model.Name)) {
                    Index.Add(model.Name, model);
                } else {
                    Console.WriteLine("Duplicated entry: " + model.Name);
                }
            }
        }

        /// <summary>
        /// Get the next token
        /// </summary>
        /// <param name="source"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static string NextToken(string source, int start) {
            int count = 0;
            start = SkipComments(source, start);
            for (;
                char.IsLetterOrDigit(source[start + count]) ||
                source[start + count] == '_'; count++) ;

            return source.Substring(start, count);
        }
        
        /// <summary>
        /// Skips a section of comment begins with (* and ends with *).
        /// </summary>
        public static int SkipComments(string source, int start) {
            char chr = source[start];
            StringBuilder buffer = new StringBuilder();

            if (chr == '(') {
                char cxx = source[start + 1];
                if (cxx == '*') {
                    start++;
                    buffer.Append(chr).Append(cxx);
                    while (start + 1 < source.Length) {
                        cxx = source[++start];
                        buffer.Append(cxx);
                        if (cxx == '*' && source[start + 1] == ')') {
                            cxx = source[++start];
                            buffer.Append(cxx);
                            break;
                        }
                    }
                }
            }

            return start; // buffer.ToString();
        }

        #region IClassFinder Members

        public void AddClassPath(string workspace) {
            Scan(workspace);
        }

        public string[] GetAllWorkspaces() {
            return Workspaces.ToArray();
        }

        public string GetLocation(string className) {
            ModelFile file = Index[className];
            if (file != null) {
                return file.FileName;
            }

            throw new FileNotFoundException(className);
        }

        public string GetSource(string className) {
            ModelFile file = Index[className];
            if (file != null) {
                return file.Source;
            }

            throw new FileNotFoundException(className);
        }

        #endregion
    }
}
